package org.compiere.model;

import java.awt.Container;
import java.io.File;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;

import javax.swing.JFrame;

import org.compiere.apps.IProcessParameter;
import org.compiere.apps.ProcessCtl;
import org.compiere.apps.ProcessParameterPanel;
import org.compiere.apps.Waiting;
import org.compiere.process.DocAction;
import org.compiere.process.DocumentEngine;
import org.compiere.process.ProcessInfo;
import org.compiere.process.ProcessInfoParameter;
import org.compiere.util.ASyncProcess;
import org.compiere.util.CCache;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;

public class Mhrmvacation extends X_hrm_vacation implements DocAction, ASyncProcess{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1704596304124584382L;
	
	private int AD_Process_ID;
	/**/
	ASyncProcess m_parent;

	
	/**	Static Logger	*/
	private static CLogger	s_log	= CLogger.getCLogger (Mhrmvacation.class);

	/** Reversal Flag		*/
	private boolean m_reversal = false;
	/**
	 * 	Is Reversal
	 *	@return reversal
	 */
	public boolean isReversal()
	{
		return m_reversal;
	}	//	isReversal

	/**
	 * 	Set Reversal
	 *	@param reversal reversal
	 */
	private void setReversal(boolean reversal)
	{
		m_reversal = reversal;
	}	//	setReversal

	public Mhrmvacation(Properties ctx, int hrm_vacation_ID, String trxName) {
		super(ctx, hrm_vacation_ID, trxName);
		if (hrm_vacation_ID == 0)
		{
			setDocStatus (DOCSTATUS_Drafted);		//	Draft
			setDocAction (DOCACTION_Complete);
			//
//			setPaymentRule(PAYMENTRULE_OnCredit);	//	Payment Terms
//
//			setDateInvoiced (new Timestamp (System.currentTimeMillis ()));
			setDateAcct (getDateFrom());
			setC_DocType_ID(1000063);
//			setChargeAmt (Env.ZERO);
//			setTotalLines (Env.ZERO);
//			setGrandTotal (Env.ZERO);
//			//
			setIsSOTrx (true);
//			setIsTaxIncluded (false);
			setIsApproved (false);
//			setIsDiscountPrinted (false);
//			setIsPaid (false);
//			setSendEMail (false);
//			setIsPrinted (false);
			setIsTransferred (false);
//			setIsSelfService(false);
//			setIsPayScheduleValid(false);
//			setIsInDispute(false);
			setPosted(false);
			super.setProcessed (false);
			setProcessing(false);
//			setDocumentNo(getName());
//			save();
		}
	}
	
	public Mhrmvacation(Properties ctx, ResultSet rs, String trxName) {
		super(ctx, rs, trxName);
		// 
	}

	
	//* Select employee work time from some date */
	public static int getWorkTime(int C_BPartner_ID, Timestamp sdt,	Timestamp edt) {
		
	    PreparedStatement pstmt = null;
		ResultSet rs = null;
		int result = 0;
			
		// Employee data from the time sheets
		String sql_ = "SELECT COUNT(*) FROM HRM_WORKHOURQUOTA_V \n " +
						"WHERE AD_CLIENT_ID=" + Env.getAD_Client_ID(Env.getCtx()) + "\n " +
							"AND C_BPARTNER_ID=" + C_BPartner_ID + "\n " +
							"AND HRM_DATEFROM BETWEEN " + DB.TO_DATE(sdt) + " AND " + DB.TO_DATE(edt) + " ";
			try
			{
				pstmt = DB.prepareStatement(sql_,null);				
				rs = pstmt.executeQuery();	
				if(rs.next())
					result = rs.getInt(1);
			}
			catch (SQLException e)
			{
				s_log.log(Level.SEVERE, "product", e);
			}
			finally
			{
				DB.close(rs, pstmt);
				rs = null; pstmt = null;
			}	
		return result;
	}
	
	/** Load Model for HRM Salary Employee */
	public static Double getMaterialAid(int C_BPartner_ID, Timestamp dt, boolean isActive){
		
		if(C_BPartner_ID == 0)
    		return null;
		String active = "Y";
    	if(!isActive)
    		active = "N";
		// 	
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		Double result = .0;
			
		/** Select amount salary for take material aid sum for date period */
		String sql_ = "SELECT HRM_AMOUNTSALARY FROM HRM_SALARYEMPLOYEE \n "   +
						"WHERE ISACTIVE='" + active + "' \n " +
							"AND AD_CLIENT_ID=" + Env.getAD_Client_ID(Env.getCtx()) + "\n " +
							"AND C_BPARTNER_ID=" + C_BPartner_ID + "\n " +
							"AND " + DB.TO_DATE(dt) + " BETWEEN STARTDATE AND ENDDATE ";
			try
			{
				pstmt = DB.prepareStatement(sql_,null);				
				rs = pstmt.executeQuery();
				if(rs.next())
					result = rs.getDouble(1);			
			}
			catch (SQLException e)
			{
				s_log.log(Level.SEVERE, "product", e);
			}
			finally
			{
				DB.close(rs, pstmt);
				rs = null; pstmt = null;
			}

		return result;
	}
	
	/**	Vacation Lines	*/
	private Mhrmvacationline[] m_lines;
	private X_hrm_vacationline[] m_linesacct;
	
	/**
	 * 	Get HR Vacation Lines
	 * 	@param whereClause starting with AND
	 * 	@return lines
	 */
	private Mhrmvacationline[] getLinesVacation (String whereClause)
	{
		String whereClauseFinal = "hrm_vacation_ID=? ";
		if (whereClause != null)
			whereClauseFinal += whereClause;
		List<Mhrmvacationline> list = new Query(getCtx(), I_hrm_vacation_line.Table_Name, whereClauseFinal, get_TrxName())
										.setParameters(gethrm_vacation_ID())
										.list();
		return list.toArray(new Mhrmvacationline[list.size()]);
	}	//	getLines

	/**
	 * 	Get Vacation Lines
	 * 	@param requery
	 * 	@return lines
	 */
	public Mhrmvacationline[] getLinesVacation (boolean requery)
	{
		if (m_lines == null || m_lines.length == 0 || requery)
			m_lines = getLinesVacation(null);
		set_TrxName(m_lines, get_TrxName());
		return m_lines;
	}	//	getLines

	/**
	 * 	Get Vacation line
	 * 	@return lines
	 */
	public Mhrmvacationline[] getLinesVacation()
	{
		return getLinesVacation(false);
	}	//	getLines
	
//	 X_hrm_vacationline {
	/**
	 * 	Get HR Vacation Lines
	 * 	@param whereClause starting with AND
	 * 	@return lines
	 */
	private X_hrm_vacationline[] getLinesVacationAcct (String whereClause)
	{
		String whereClauseFinal = "hrm_vacation_ID=? ";
		if (whereClause != null)
			whereClauseFinal += whereClause;
		List<X_hrm_vacationline> list = new Query(getCtx(), I_hrm_vacationline.Table_Name, whereClauseFinal, get_TrxName())
										.setParameters(gethrm_vacation_ID())
										.list();
		return list.toArray(new X_hrm_vacationline[list.size()]);
	}	//	getLines

	/**
	 * 	Get Vacation Lines
	 * 	@param requery
	 * 	@return lines
	 */
	public X_hrm_vacationline[] getLinesVacationAcct (boolean requery)
	{
		if (m_linesacct == null || m_linesacct.length == 0 || requery)
			m_linesacct = getLinesVacationAcct(null);
		set_TrxName(m_linesacct, get_TrxName());
		return m_linesacct;
	}	//	getLines

	/**
	 * 	Get Vacation line
	 * 	@return lines
	 */

	public X_hrm_vacationline[] getLinesVacationAcct()
	{
		return getLinesVacationAcct(false);
	}	//	getLines
	
	public X_hrm_vacationline getVacationLine(int hrm_reservesectional_id) {
		X_hrm_vacationline result = null;
		getLinesVacationAcct();
		for(X_hrm_vacationline l:m_linesacct) {
			if (l.gethrm_reservesectional_ID() ==  hrm_reservesectional_id) {
				result = l;
				break;
			}
		}
		return result;
	}
	
//	 } X_hrm_vacationline 
			
	public void setProcessed (boolean processed)
	{
		super.setProcessed (processed);
		if (get_ID() == 0)
			return;
		String set = "SET Processed='"
			+ (processed ? "Y" : "N")
			+ "' WHERE hrm_vacation_ID = " + gethrm_vacation_ID();
		int succes = DB.executeUpdate("UPDATE hrm_vacation " + set, get_TrxName());
//		int noTax = DB.executeUpdate("UPDATE C_InvoiceTax " + set, get_TrxName());
//		m_lines = null;
//		m_taxes = null;
//		log.fine(processed + " - Lines=" + noLine + ", Tax=" + noTax);
	}	//	setProcessed
	
	 
	public String prepareIt()
	{
		log.info(toString());
		m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE);
		if (m_processMsg != null)
			return DocAction.STATUS_Invalid;

		//	Convert/Check DocType
		if (getC_DocType_ID() == 0)
		{
			m_processMsg = "No Document Type";
			return DocAction.STATUS_Invalid;
		}



		m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE);
		if (m_processMsg != null)
			return DocAction.STATUS_Invalid;

		//	Add up Amounts
		m_justPrepared = true;
		if (!DOCACTION_Complete.equals(getDocAction()))
			setDocAction(DOCACTION_Complete);
		return DocAction.STATUS_InProgress;
	}	//	prepareIt	
	
	public boolean processIt (String processAction)
	{
		m_processMsg = null;
		DocumentEngine engine = new DocumentEngine (this, getDocStatus());
		return engine.processIt (processAction, getDocAction());
	}	//	process

	/**	Process Message 			*/
	private String		m_processMsg = null;
	/**	Just Prepared Flag			*/
	private boolean		m_justPrepared = false;

	private Waiting m_waiting;
	
	@Override
	public void lockUI(ProcessInfo pi) {
		JFrame frame = Env.getFrame((Container)m_parent);
		m_waiting = new Waiting (frame, Msg.getMsg(Env.getCtx(), "Processing"), false, pi.getEstSeconds());
		
	}

	@Override
	public void unlockUI(ProcessInfo pi) {

		if (m_waiting != null)
			m_waiting.dispose();
		m_waiting = null;	}

	@Override
	public boolean isUILocked() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void executeASync(ProcessInfo pi) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public boolean unlockIt() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean invalidateIt() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean approveIt() {
		log.info(toString());
		setIsApproved(true);
		return true;
	}

	@Override
	public boolean rejectIt() {
		// TODO Auto-generated method stub
		return false;
	}

	
	protected void getAD_Process_ID() {
		
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		// 
		String sql_ = "select distinct t.ad_process_id from ad_process t \n "+ 
					  "where (lower(t.value) like lower('GenerateAmountSalary%') or lower(t.name) like lower('GenerateAmountSalary%')) "+ 
				      "and t.isactive='Y'";
		try
		{
			pstmt = DB.prepareStatement(sql_, null);
			rs = pstmt.executeQuery();
			if (rs.next())
				AD_Process_ID = rs.getInt(1);
		}
		catch (SQLException e)
		{
			log.log(Level.SEVERE, "product", e);
		}
		finally
		{
			DB.close(rs, pstmt);
			rs = null; pstmt = null;
		}
	}	
	@Override
	public String completeIt() {
		getAD_Process_ID();
		
		if(AD_Process_ID != 0){
			//  Prepare Process
			ProcessInfo pi = new ProcessInfo ("The printing process of ", AD_Process_ID);
			pi.setAD_User_ID (Env.getAD_User_ID(Env.getCtx()));
			pi.setAD_Client_ID(Env.getAD_Client_ID(Env.getCtx()));
			pi.setRecord_ID(get_ID());

			List<ProcessInfoParameter> po = new ArrayList<ProcessInfoParameter>();
			po.add(new ProcessInfoParameter("CloseTax","Y",null,"",""));
			//
			ProcessInfoParameter[] pp = new ProcessInfoParameter[po.size()];
			po.toArray(pp);
			pi.setParameter(pp);
			//	Execute Process
			ProcessParameterPanel pu = new ProcessParameterPanel(0, pi);
			ProcessCtl.process(this, 0, (IProcessParameter) pu, pi, null);
		}
		
		//	Re-Check
		if (!m_justPrepared)
		{
			String status = prepareIt();
			if (!DocAction.STATUS_InProgress.equals(status))
				return status;
		}

		m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE);
		if (m_processMsg != null)
			return DocAction.STATUS_Invalid;
//
//		//	Implicit Approval
		if (!isApproved())
			approveIt();
		log.info(toString());
		
		StringBuffer info = new StringBuffer();
		
		m_processMsg = info.toString().trim();
		setProcessed(true);
//		save();
		setDocAction(DOCACTION_Close);
		return DocAction.STATUS_Completed;	}

	@Override
	public boolean voidIt() {
		boolean result = true;
		if (!haveCorrectDocument()) {
			if ( DocAction.STATUS_Completed.equals(getDocStatus()) && isPosted()) {
				result = reverseCorrectIt();
			}
			if (result) {
				setProcessed(true);
				setDocAction(DOCACTION_None);
			}
		} else {
			result = false;
		}
		return result;	
	}

	@Override
	public boolean closeIt() {
		log.info(toString());
		// Before Close
		m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE);
		if (m_processMsg != null)
			return false;

		setProcessed(true);
		setDocAction(DOCACTION_None);

		// After Close
		m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE);
		if (m_processMsg != null)
			return false;
		return true;	}

	@Override
	public boolean reverseCorrectIt() {
		boolean result = false;
		log.info(toString());
		if (!haveCorrectDocument()) {
			result = createCorrectDocument();
		}
		return result;
	}

	/**
	 * @return
	 */
	private boolean createCorrectDocument() {
		Mhrmvacation reversal = copyFrom (this, getDateFrom(), getDateAcct(),getC_DocType_ID(), get_TrxName());
		if (reversal == null) {
			m_processMsg = "Could not create Invoice Reversal";
			return false;
		}
		
		reversal.setReversal(true);
		reversal.setName("!!!!-" + reversal.getName());
		reversal.addDescription("{->" + getDocumentNo() + ")");
		
		//TODO Добавить обратные суммы
		
		Mhrmvacationline[] iLines = reversal.getLinesVacation();
		for (int i = 0; i < iLines.length; i++)
		{
			Mhrmvacationline iLine = iLines[i];
			iLine.sethrm_amountvacation(iLine.gethrm_amountvacation().negate());
			iLine.sethrm_average(iLine.gethrm_average().negate());
			iLine.sethrm_OPV(iLine.gethrm_OPV().negate());
			iLine.sethrm_COtch(iLine.gethrm_COtch().negate());
			iLine.sethrm_IPN(iLine.gethrm_IPN().negate());
			iLine.sethrm_CNalog(iLine.gethrm_CNalog().negate());
			iLine.sethrm_total(iLine.gethrm_total().negate());
			iLine.sethrm_materialaid(iLine.gethrm_materialaid().negate());
			iLine.saveUpdate();
			
			X_hrm_vacationline[] jLines = iLine.getVacationLinesAcct();
			for(int j=0; j < jLines.length; j++ ) {
				X_hrm_vacationline jLine = jLines[j];
				jLine.sethrm_amount(jLine.gethrm_amount().negate());
				jLine.saveUpdate();
			}
			
        }
		
		setProcessed(true);
		
		reversal.setReversal_ID(gethrm_vacation_ID()); // TODO
		reversal.saveEx(get_TrxName());
		
		if (!reversal.processIt(DocAction.ACTION_Complete))	{
			m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg();
			return false;
		}
		reversal.closeIt();
		reversal.setProcessing (false);
		reversal.setDocStatus(DOCSTATUS_Drafted);
		reversal.setDocAction(DOCACTION_Complete);
		reversal.saveEx(get_TrxName());
		m_processMsg = reversal.getDocumentNo();
		setReversal_ID(reversal.gethrm_vacation_ID());
		addDescription("(" + reversal.getDocumentNo() + "<-)");
		return true;
		
	}

	/**
	 * @return
	 */
	public boolean haveCorrectDocument() {
		boolean result = getReversal_ID() > 0;
		return result;
	}

	@Override
	public boolean reverseAccrualIt() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean reActivateIt() {
		setDocStatus(STATUS_Drafted);
		setDocAction(DOCACTION_Complete);
		setProcessed(false);
		setPosted(false);
		return true;
	}

	@Override
	public String getSummary() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getDocumentInfo() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public File createPDF() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getProcessMsg() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getDoc_User_ID() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public BigDecimal getApprovalAmt() {
		// TODO Auto-generated method stub
		return null;
	}

	public static Mhrmvacation get (Properties ctx, int hrm_vacation_ID)
	{
		Integer key = new Integer (hrm_vacation_ID);
		Mhrmvacation retValue = (Mhrmvacation) s_cache.get (key);
		if (retValue != null)
			return retValue;
		retValue = new Mhrmvacation (ctx, hrm_vacation_ID, null);
		if (retValue.get_ID () != 0)
			s_cache.put (key, retValue);
		return retValue;
	} //	get

	/**	Cache						*/
	private static CCache<Integer,Mhrmvacation>	s_cache	= new CCache<Integer,Mhrmvacation>("hrm_vacation", 20, 2);	//	2 minutes

	@Override
	public Timestamp getDateAcct () 
	{
		if (super.getDateAcct() == null) {
			setDateAcct (getDateFrom());
		}
		return super.getDateAcct();
	}
	
	public static Mhrmvacation copyFrom(Mhrmvacation from, Timestamp dateDoc, Timestamp dateAcct, int C_DocTypeTarget_ID, String trxName) {
		Mhrmvacation to = new Mhrmvacation(from.getCtx(),0,trxName);
		copyValues(from, to);
		to.set_ValueNoCheck ("hrm_vacation_ID", I_ZERO);
		to.set_ValueNoCheck ("DocumentNo", null);
		//
		to.setDocStatus (DOCSTATUS_Drafted);		//	Draft
		to.setDocAction(DOCACTION_Complete);
		//
		to.setDateFrom (dateDoc);
		to.setDateAcct (dateAcct);
		//
		to.setIsApproved (false);
		//
		to.setIsTransferred (false);
		to.setPosted (false);
		to.setProcessed (false);
		//[ 1633721 ] Reverse Documents- Processing=Y
		to.setProcessing(false);
		//	delete references
		to.saveEx(trxName);
		if (from.getLinesVacation().length > 0) {
			if (to.copyListLinesFrom(from) == 0)
				throw new IllegalStateException("Could not create List");
		}
		
		return to;
	}
	
	public int copyListLinesFrom (Mhrmvacation other) {
		if (isProcessed() || isPosted() || other == null)
			return 0;
		Mhrmvacationline[] fromLines = other.getLinesVacation(false);
		int count = 0;
		for (int i = 0; i < fromLines.length; i++) {
			Mhrmvacationline line = new Mhrmvacationline(getCtx(), 0, get_TrxName());
			Mhrmvacationline fromLine = fromLines[i];
			PO.copyValues (fromLine, line, fromLine.getAD_Client_ID(), fromLine.getAD_Org_ID());
			line.sethrm_vacation_ID(gethrm_vacation_ID());
			line.set_ValueNoCheck ("hrm_vacation_line_ID", I_ZERO);	// new
			if (line.save(get_TrxName())) {
				count++;
				if (fromLine.getVacationLinesAcct().length > 0) {
					if (line.copyVacationLinesFrom(fromLine) == 0) {
						throw new IllegalStateException("Could not create List");
					}
				}
			}
			
		}
		if (fromLines.length != count) {
			log.log(Level.SEVERE, "Line difference - From=" + fromLines.length + " <> Saved=" + count);
		} 
		return count;
	}

	public void addDescription (String description)
	{
		String desc = getDescription();
		if (desc == null)
			setDescription(description);
		else
			setDescription(desc + " | " + description); 
	}	//	addDescription 
}
